package com.wz.boot.core;

import com.wz.boot.annotation.ExitMethod;
import com.wz.boot.annotation.Function;
import com.wz.boot.annotation.FunctionScan;
import com.wz.boot.annotation.Handle;
import com.wz.boot.function.defaultchat.DefaultChat;
import com.wz.boot.pojo.ClassProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 对项目进行初始化操作，具体初始化的内容包括
 * 1.扫描功能类，并加载功能类；功能类指的是含有@Function的类
 * 2.加载功能介绍文本
 * 3.加载完整功能介绍文本
 * 4.
 */
@FunctionScan("com.wz.boot.function")
@Component
public class ProjectFunctionScanAndInitCore {
    private static Logger logger = LoggerFactory.getLogger(ProjectFunctionScanAndInitCore.class);


    public static ConfigurableApplicationContext configurableApplicationContext;

    private static List<Class> functionClassList = new ArrayList<>();



    /*
        扫描功能类的父路径
        在linux下会读取到这种格式的路径: /app/qqzone_send/2021.6.14.test_3.jar!/BOOT-INF/classes!/
     */
    private static String fileParentPath = null;

    private static List<Class> validFunctionClassList = new ArrayList<>();

    //存放的是当前系统中的所有功能函数 key:function.priority()  value:function
    private static Map<Integer,Function> functionMap = new HashMap<>();

    //将功能类代码集中到这个集合中，方便后面调用与反射
    private static List<ClassProperty> classPropertyList = new ArrayList<>();

    //功能介绍文本
    private static String simpleFunctionText = null;

    //全部功能介绍文本
    private static String allFunctionText = null;

    /**
     * 加载功能函数用的
     * 1、因linux系统无法读取jar, 固改变策略，采用windows和linux分别采取一套扫描策略
     * @throws ClassNotFoundException
     */
    public static void loadFunction(){
        String osName = System.getProperty("os.name");
        String userDir = System.getProperty("user.dir");
        logger.info("当前项目部署的操作系统名:os.name:{}     功能目录:user.dir:{}", osName, userDir);
        FunctionScan functionScan = ProjectFunctionScanAndInitCore.class.getDeclaredAnnotation(FunctionScan.class);
        if(osName == null || osName.isEmpty()){
            logger.info("扫描停止");
            return;
        }
        osName = osName.toLowerCase();
        if(osName.startsWith("window")){
            fileParentPath = userDir + "\\src\\main\\java\\";
            String functionScanPath = functionScan.value();
            //扫描完成，所有的功能类代码将被加载到functionClassList中
            classScanForWindows(functionScanPath);
        }else if(osName.startsWith("linux")){
            classScanForLinux("");
            //  file:/app/qqzone_send/2021.6.14.test_3.jar!/BOOT-INF/classes!/
//            String path = new MyInit().getClass().getResource("/").getPath();
        }else{
            logger.error("---------------------------错误的osName：{}-------------------------------",osName);
            System.exit(0);
        }

        //筛选有效的功能类到validFunctionClassList中
        filterValidFunctionClass();

        //开始校验
        boolean checkFlag = check();
        logger.info("功能函数校验结果:" + (checkFlag ? "通过" : "不通过"));
        if(!checkFlag){
            System.out.println("------------------------------------请检查校验错误原因------------------------------------");
            System.exit(0);
        }
    }



    public static void init(){
        //加载所有有效的功能类
        loadFunction();
        //加载功能介绍
        loadIntroduce();

    }

    /**
     * 加载功能介绍
     */
    private static void loadIntroduce(){
        //功能介绍
        StringBuffer simpleFunctionStringBufferText = new StringBuffer();
        //全部功能介绍
        StringBuffer allFunctionStringBufferText = new StringBuffer();
        //遍历，初始化操作
        for (Class cla : validFunctionClassList) {
            Function function = (Function) cla.getDeclaredAnnotation(Function.class);
            if(function.priority() == 0){
                //跳过默认功能
                continue;
            }
            //得到cla中function的属性
            int priority = function.priority();
            String name = function.name();
            String describe = function.describe();
            String touch = function.touch();
            boolean exit = function.execExit();
            String claName = function.claName();

            //一定要注意，不能自己实例化,要从ioc容器中获取
            Object bean = configurableApplicationContext.getBean(cla);
            Method[] methods = cla.getMethods();

            //单一功能记录类
            ClassProperty classProperty = new ClassProperty(priority,name,describe,touch,exit,bean,claName);
            for (Method method : methods) {
                Handle handle = method.getDeclaredAnnotation(Handle.class);
                //加载cla的所有有效方法-放置到单一功能记录类
                if(handle != null && handle.valid()){
                    classProperty.putMethod(method);
                }
                //加载cla的默认方法
                if(handle != null && handle.defaultHandle()){
                    classProperty.setDefaultMethod(method);
                }
                //加载cla的退出方法
                ExitMethod exitMethod = method.getDeclaredAnnotation(ExitMethod.class);
                if(classProperty.getExitMethod() == null && exitMethod != null && exitMethod.valid()){
                    classProperty.setExitMethod(method);
                }else if(classProperty.getExitMethod() != null && exitMethod != null && exitMethod.valid()){
                    logger.error("{} 中发现两个有效的退出方法", cla.getPackageName());
                }
            }
            //如果没有默认方法, 则设置classProperty.getMethodList()中的第一个方法为默认方法
            if(classProperty.getDefaultMethod() == null && classProperty.getMethodList().size() == 1){
                Method method = classProperty.getMethodList().get(0);
                classProperty.setDefaultMethod(method);
            }
            //方便加载功能介绍的
            functionMap.put(priority,function);//priority从1开始
            //记住此功能
            classPropertyList.add(classProperty);
        }
        //加载功能介绍
        for(Integer i : functionMap.keySet()){
//        for (int i = 1; i <= functionMap.size(); i++) {
            Function function = functionMap.get(i);
            int priority = function.priority();
            String name = function.name();
            String describe = function.describe();
            String touch = function.touch();
            boolean exit = function.execExit();
            String singleFunctionText = String.format("%s", touch);
            String allSingleFunctionText = String.format("%s. [%s]; 进入指令:[%s]; 功能描述: [%s]", priority, name, touch, describe);
            simpleFunctionStringBufferText.append(singleFunctionText).append("\n\n");
            allFunctionStringBufferText.append(allSingleFunctionText).append("\n\n");
        }
        simpleFunctionText = simpleFunctionStringBufferText.append("查询完整功能菜单请输入[完整功能查询]或[all]\n").append("退出功能请使用指令[退出]\n").insert(0,"下面是我所支持的功能：\n\n").toString();
        allFunctionText = allFunctionStringBufferText.append("查询功能菜单请输入[功能查询]或[[fs]").append("退出功能请使用指令[退出]\n").insert(0,"下面是我所支持的功能：\n\n").toString();
    }


    /**
     * 筛选所有有效的功能类
     */
    private static void filterValidFunctionClass() {
        for (Class cla : functionClassList) {
            Annotation annotation = cla.getDeclaredAnnotation(Function.class);
            if (annotation != null && annotation instanceof Function) {
                //必须是有效的
                if (((Function) annotation).valid()) {
                    validFunctionClassList.add(cla);
                }
            }
        }
    }

    /**
     * 扫描windows操作系统中的功能类
     * @param functionScanPath
     * @throws ClassNotFoundException
     */
    private static void classScanForWindows(String functionScanPath) {
        String filePath= fileParentPath + functionScanPath.replace(".",File.separator);
        logger.info("当前扫描windows系统中功能类路径：{}", filePath);
        try {
            File file = new File(filePath);
            logger.info("file存在: {}  路径：{}",file.exists(), file.getAbsolutePath());
            fileScannerForWindows(file);
        }catch (Exception e){
            logger.error("扫描功能类代码出现错误: {} ",e.getMessage());
            e.printStackTrace();
        }

    }

    /**
     * windows递归扫描功能类
     * @param file
     * @throws ClassNotFoundException
     */
    private static void fileScannerForWindows(File file) {
        if (file.isFile() && file.getName().endsWith(".java") && file.getName().length() >= 6) {//java类必须至少有一位字母作为类名
            String filePath = file.getAbsolutePath();
            String qualifiedName= filePath.substring(fileParentPath.length(), filePath.length() - 5).replace('\\', '.');
            logger.debug("扫描到类文件: {},  存在：{}",qualifiedName, file.exists());
            try {
                Class<?> aClass = Class.forName(qualifiedName);
//                Class<?> aClass = Class.forName("com.wz.boot.function.smile.Smile");
                functionClassList.add(aClass);
            } catch (ClassNotFoundException e) {
                logger.info("类文件未找到:" + e.getMessage()+ "文件名：" + qualifiedName);
            }
        }else if(file.isDirectory()) {
            logger.debug("扫描到文件夹文件: {},  存在：{}",file.getAbsolutePath(), file.exists());
            for(File f : file.listFiles())
                fileScannerForWindows(f);
        }
    }


    /*
    * 扫描linux操作系统中的功能类
    * todo 应该加紧改进此功能 以达到可以自动识别linux  jar 中类的路径，并自动配置的目的
    * */
    private static void classScanForLinux(String functionScanPath){
        String filePath= fileParentPath + functionScanPath.replace(".",File.separator);
        logger.info("当前扫描linux系统中功能类路径：{}", filePath);
        try {

            functionClassList.add(Class.forName("com.wz.boot.function.boast.Boast"));
            functionClassList.add(Class.forName("com.wz.boot.function.chatwithstranger.ChatWithStranger"));
            functionClassList.add(Class.forName("com.wz.boot.function.naowan.NaoWan"));
            functionClassList.add(Class.forName("com.wz.boot.function.defaultchat.DefaultChat"));
            functionClassList.add(Class.forName("com.wz.boot.function.repeatchicken.RepeatChicken"));
            functionClassList.add(Class.forName("com.wz.boot.function.smile.Smile"));
            functionClassList.add(Class.forName("com.wz.boot.function.weather.Weather"));
        }catch (Exception e){
            logger.error("错误信息：{}   错误原因：{}", e.getMessage(), e.getCause());
        }
    }


    /**
     * 得到所有功能类Cla集合
     * @return
     */
    public static List<Class> getFunctionClassList(){
        return functionClassList;
    }
    /**
     * 得到所有有效功能类Cla集合
     * 有效指的是 Function注解下：valid = true
     * @return
     */
    public static List<Class> getValidFunctionClassList(){
        return validFunctionClassList;
    }

    /**
     * 校验基于注解的功能配置是否正确。
     * 具体的校验的内容包含：
     * 1.
     * @return 校验结果
     */
    public static boolean check(){
        //校验结果标志
        boolean checkFlag = true;
        //先筛选有Function注解的功能类
        for (Class cla : validFunctionClassList) {
            Function functionAnnotation = (Function) cla.getDeclaredAnnotation(Function.class);
            Method[] methods = cla.getMethods();
            //是否找到handle方法的表示
            boolean hasFindHandleFlag = false;
            //function类中handle方法的数量
            int countHandle = 0;
            for (Method method : methods) {
                Handle handle = method.getDeclaredAnnotation(Handle.class);
                if(handle != null && handle.valid()){
                    hasFindHandleFlag = true;
                    countHandle++;
                }
            }
            if(!hasFindHandleFlag){
                if(functionAnnotation != null){
                    logger.error("功能:[" + functionAnnotation.name() + "] 缺失默认的Handle函数");
                    //从validFunctionClassList移除它
                    validFunctionClassList.remove(cla);
                }
            }else{
                logger.info("扫描到功能类: {}, 共有{}个handle函数",cla.getName(), countHandle);
            }

        }
        logger.info("发现{}个有效功能类",validFunctionClassList.size());
        //校验功能类的优先级是否有重复
        Map<Integer,Function> priorityMap = new HashMap<>();
        Map<String,Function> touchMap = new HashMap<>();
        for (Class cla : validFunctionClassList) {
            Annotation annotation = cla.getDeclaredAnnotation(Function.class);
            Function functionAnnotation = (Function) annotation;
            int priority = functionAnnotation.priority();
            String touch = functionAnnotation.touch();
            boolean jump = false;
            if(priorityMap.containsKey(priority)){
                Function function = priorityMap.get(priority);
                logger.info("功能: [{}], 类：{} 优先级: [{}] 与功能: [{}}], 类:{}  优先级: [{}]   优先级重复",function.name(), function.claName(),priority,functionAnnotation.name(),cla.getSimpleName(),priority);
                jump = true;
            }
            if(touchMap.containsKey(touch)){
                Function function = touchMap.get(touch);
                logger.info("功能: [{}], 类：{}  触发语句: [{}] 与功能: [{}}], 类:{}  触发语句: [{}]   触发语句重复",function.name(), function.claName(),touch,functionAnnotation.touch(), cla.getSimpleName(),touch);
                jump = true;
            }
            if(jump){
                checkFlag = false;
                continue;
            }
            priorityMap.put(priority,functionAnnotation);
            touchMap.put(touch,functionAnnotation);
        }
        if(priorityMap.keySet().size() == 0 || touchMap.keySet().size() == 0){
            logger.warn("没有发现有效的功能代码");
            return false;
        }
        //校验功能类下必须有默认的Handle函数
        for (Class cla : functionClassList) {
            Annotation annotation = cla.getDeclaredAnnotation(Function.class);
            Function functionAnnotation = null;
            if(annotation != null && annotation instanceof  Function){
                functionAnnotation = (Function) annotation;
            }else{
                continue;
            }
            //如果functionAnnotation为空, 或者valid为无效,将跳过此类
            if(functionAnnotation == null || !functionAnnotation.valid()){
                continue;
            }
        }
        return checkFlag;
    }


    public  void setFunctionClassList(List<Class> functionClassList) {
        this.functionClassList = functionClassList;
    }

    public void setValidFunctionClassList(List<Class> validFunctionClassList) {
        this.validFunctionClassList = validFunctionClassList;
    }

    public Map<Integer, Function> getFunctionMap() {
        return functionMap;
    }

    public void setFunctionMap(Map<Integer, Function> functionMap) {
        this.functionMap = functionMap;
    }



    public static List<ClassProperty> getClassPropertyList() {
        return classPropertyList;
    }

    public static void setClassPropertyList(List<ClassProperty> classPropertyList) {
        ProjectFunctionScanAndInitCore.classPropertyList = classPropertyList;
    }

    public static String getSimpleFunctionText() {
        return simpleFunctionText;
    }

    public static void setSimpleFunctionText(String simpleFunctionText) {
        ProjectFunctionScanAndInitCore.simpleFunctionText = simpleFunctionText;
    }

    public static String getAllFunctionText() {
        return allFunctionText;
    }

    public static void setAllFunctionText(String allFunctionText) {
        ProjectFunctionScanAndInitCore.allFunctionText = allFunctionText;
    }
}
